Make read-char-by-name signal an error for invalid input.
[bpt/emacs.git] / src / editfns.c
index f86e669..bb5cc43 100644 (file)
@@ -21,10 +21,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <config.h>
 #include <sys/types.h>
 #include <stdio.h>
-#include <setjmp.h>
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
+#include <grp.h>
 #endif
 
 #include <unistd.h>
@@ -44,7 +44,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <sys/resource.h>
 #endif
 
-#include <ctype.h>
 #include <float.h>
 #include <limits.h>
 #include <intprops.h>
@@ -59,10 +58,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "window.h"
 #include "blockinput.h"
 
-#ifndef USE_CRT_DLL
-extern char **environ;
-#endif
-
 #define TM_YEAR_BASE 1900
 
 #ifdef WINDOWSNT
@@ -70,7 +65,7 @@ extern Lisp_Object w32_get_internal_run_time (void);
 #endif
 
 static Lisp_Object format_time_string (char const *, ptrdiff_t, EMACS_TIME,
-                                      int, struct tm *);
+                                      bool, struct tm *);
 static int tm_diff (struct tm *, struct tm *);
 static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
 
@@ -84,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)
@@ -102,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
@@ -251,11 +258,11 @@ The return value is POSITION.  */)
 
 
 /* Return the start or end position of the region.
-   BEGINNINGP non-zero means return the start.
+   BEGINNINGP means return the start.
    If there is no region active, signal an error. */
 
 static Lisp_Object
-region_limit (int beginningp)
+region_limit (bool beginningp)
 {
   Lisp_Object m;
 
@@ -269,7 +276,7 @@ region_limit (int beginningp)
     error ("The mark is not set now, so there is no region");
 
   /* Clip to the current narrowing (bug#11770).  */
-  return make_number ((PT < XFASTINT (m)) == (beginningp != 0)
+  return make_number ((PT < XFASTINT (m)) == beginningp
                      ? PT
                      : clip_to_bounds (BEGV, XFASTINT (m), ZV));
 }
@@ -366,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 = WGET (XWINDOW (object), buffer);
+    object = XWINDOW (object)->buffer;
 
   if (!BUFFERP (object))
     /* pos-property only makes sense in buffers right now, since strings
@@ -440,12 +447,12 @@ get_pos_property (Lisp_Object position, register Lisp_Object prop, Lisp_Object o
    BEG_LIMIT and END_LIMIT serve to limit the ranged of the returned
    results; they do not effect boundary behavior.
 
-   If MERGE_AT_BOUNDARY is nonzero, then if POS is at the very first
+   If MERGE_AT_BOUNDARY is non-nil, then if POS is at the very first
    position of a field, then the beginning of the previous field is
    returned instead of the beginning of POS's field (since the end of a
    field is actually also the beginning of the next input field, this
    behavior is sometimes useful).  Additionally in the MERGE_AT_BOUNDARY
-   true case, if two fields are separated by a field with the special
+   non-nil case, if two fields are separated by a field with the special
    value `boundary', and POS lies within it, then the two separated
    fields are considered to be adjacent, and POS between them, when
    finding the beginning and ending of the "merged" field.
@@ -460,10 +467,10 @@ find_field (Lisp_Object pos, Lisp_Object merge_at_boundary,
 {
   /* Fields right before and after the point.  */
   Lisp_Object before_field, after_field;
-  /* 1 if POS counts as the start of a field.  */
-  int at_field_start = 0;
-  /* 1 if POS counts as the end of a field.  */
-  int at_field_end = 0;
+  /* True if POS counts as the start of a field.  */
+  bool at_field_start = 0;
+  /* True if POS counts as the end of a field.  */
+  bool at_field_end = 0;
 
   if (NILP (pos))
     XSETFASTINT (pos, PT);
@@ -507,19 +514,19 @@ find_field (Lisp_Object pos, Lisp_Object merge_at_boundary,
 
        xxxx.yyyy
 
-     In this situation, if merge_at_boundary is true, we consider the
+     In this situation, if merge_at_boundary is non-nil, consider the
      `x' and `y' fields as forming one big merged field, and so the end
      of the field is the end of `y'.
 
      However, if `x' and `y' are separated by a special `boundary' field
-     (a field with a `field' char-property of 'boundary), then we ignore
+     (a field with a `field' char-property of 'boundary), then ignore
      this special field when merging adjacent fields.  Here's the same
      situation, but with a `boundary' field between the `x' and `y' fields:
 
        xxx.BBBByyyy
 
      Here, if point is at the end of `x', the beginning of `y', or
-     anywhere in-between (within the `boundary' field), we merge all
+     anywhere in-between (within the `boundary' field), merge all
      three fields and consider the beginning as being the beginning of
      the `x' field, and the end as being the end of the `y' field.  */
 
@@ -663,7 +670,7 @@ Field boundaries are not noticed if `inhibit-field-text-motion' is non-nil.  */)
 {
   /* If non-zero, then the original point, before re-positioning.  */
   ptrdiff_t orig_point = 0;
-  int fwd;
+  bool fwd;
   Lisp_Object prev_old, prev_new;
 
   if (NILP (new_pos))
@@ -744,17 +751,18 @@ Field boundaries are not noticed if `inhibit-field-text-motion' is non-nil.  */)
 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.
-If scan reaches end of buffer, return that position.
+With optional argument N, scan forward N - 1 lines first.
+If the scan reaches the end of the buffer, return that position.
 
-The returned position is of the first character in the logical order,
-i.e. the one that has the smallest character position.
+This function ignores text display directionality; it returns the
+position of the first character in logical order, i.e. the smallest
+character position on the line.
 
 This function constrains the returned position to the current field
-unless that would be on a different line than the original,
+unless that position would be on a different line than the original,
 unconstrained result.  If N is nil or 1, and a front-sticky field
 starts at point, the scan stops as soon as it starts.  To ignore field
-boundaries bind `inhibit-field-text-motion' to t.
+boundaries, bind `inhibit-field-text-motion' to t.
 
 This function does not move point.  */)
   (Lisp_Object n)
@@ -788,8 +796,9 @@ DEFUN ("line-end-position", Fline_end_position, Sline_end_position, 0, 1, 0,
 With argument N not nil or 1, move forward N - 1 lines first.
 If scan reaches end of buffer, return that position.
 
-The returned position is of the last character in the logical order,
-i.e. the character whose buffer position is the largest one.
+This function ignores text display directionality; it returns the
+position of the last character in logical order, i.e. the largest
+character position on the line.
 
 This function constrains the returned position to the current field
 unless that would be on a different line than the original,
@@ -817,34 +826,43 @@ This function does not move point.  */)
                              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)
 {
-  int visible = (XBUFFER (WGET (XWINDOW (selected_window), buffer))
-                == current_buffer);
-
-  return Fcons (Fpoint_marker (),
-               Fcons (Fcopy_marker (BVAR (current_buffer, mark), Qnil),
-                      Fcons (visible ? Qt : Qnil,
-                             Fcons (BVAR (current_buffer, mark_active),
-                                    selected_window))));
+  Lisp_Object save, *data = xmalloc (word_size * 4);
+
+  data[0] = Fpoint_marker ();
+  /* Do not copy the mark if it points to nowhere.  */
+  data[1] = (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.  */
+  data[2] = ((XBUFFER (XWINDOW (selected_window)->buffer) == current_buffer)
+            ? selected_window : Qnil);
+  data[3] = BVAR (current_buffer, mark_active);
+
+  save = make_save_value (data, 4);
+  XSAVE_VALUE (save)->dogc = 1;
+  return save;
 }
 
+/* Restore saved buffer before leaving `save-excursion' special form.  */
+
 Lisp_Object
 save_excursion_restore (Lisp_Object info)
 {
-  Lisp_Object tem, tem1, omark, nmark;
+  Lisp_Object tem, tem1, omark, nmark, *data = XSAVE_VALUE (info)->pointer;
   struct gcpro gcpro1, gcpro2, gcpro3;
-  int 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 (data[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,37 +870,26 @@ save_excursion_restore (Lisp_Object info)
   Fset_buffer (tem);
 
   /* Point marker.  */
-  tem = XCAR (info);
+  tem = data[0];
   Fgoto_char (tem);
   unchain_marker (XMARKER (tem));
 
   /* Mark marker.  */
-  info = XCDR (info);
-  tem = XCAR (info);
+  tem = data[1];
   omark = Fmarker_position (BVAR (current_buffer, mark));
-  Fset_marker (BVAR (current_buffer, mark), tem, Fcurrent_buffer ());
-  nmark = Fmarker_position (tem);
-  unchain_marker (XMARKER (tem));
+  if (NILP (tem))
+    unchain_marker (XMARKER (BVAR (current_buffer, mark)));
+  else
+    {
+      Fset_marker (BVAR (current_buffer, mark), tem, Fcurrent_buffer ());
+      nmark = Fmarker_position (tem);
+      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 (WGET (XWINDOW (selected_window), buffer)))
-    Fswitch_to_buffer (Fcurrent_buffer (), Qnil);
-#endif /* 0 */
-
-  /* Mark active */
-  info = XCDR (info);
-  tem = XCAR (info);
+  /* Mark active.  */
+  tem = data[3];
   tem1 = BVAR (current_buffer, mark_active);
-  BVAR (current_buffer, mark_active) = tem;
+  bset_mark_active (current_buffer, tem);
 
   /* If mark is active now, and either was not active
      or was at a different place, run the activate hook.  */
@@ -904,10 +911,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 = data[2];
+  if (WINDOWP (tem)
       && !EQ (tem, selected_window)
-      && (tem1 = WGET (XWINDOW (tem), buffer),
+      && (tem1 = XWINDOW (tem)->buffer,
          (/* Window is live...  */
           BUFFERP (tem1)
           /* ...and it shows the current buffer.  */
@@ -915,6 +922,10 @@ save_excursion_restore (Lisp_Object info)
     Fset_window_point (tem, make_number (PT));
 
   UNGCPRO;
+
+ out:
+
+  free_save_value (info);
   return Qnil;
 }
 
@@ -946,18 +957,15 @@ usage: (save-excursion &rest BODY)  */)
 }
 
 DEFUN ("save-current-buffer", Fsave_current_buffer, Ssave_current_buffer, 0, UNEVALLED, 0,
-       doc: /* Save the current buffer; execute BODY; restore the current buffer.
-Executes BODY just like `progn'.
+       doc: /* Record which buffer is current; execute BODY; make that buffer current.
+BODY is executed just like `progn'.
 usage: (save-current-buffer &rest BODY)  */)
   (Lisp_Object args)
 {
-  Lisp_Object val;
   ptrdiff_t count = SPECPDL_INDEX ();
 
-  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
-
-  val = Fprogn (args);
-  return unbind_to (count, val);
+  record_unwind_current_buffer ();
+  return unbind_to (count, Fprogn (args));
 }
 \f
 DEFUN ("buffer-size", Fbufsize, Sbufsize, 0, 1, 0,
@@ -1226,9 +1234,9 @@ of the user with that uid, or nil if there is no such user.  */)
     return Vuser_login_name;
 
   CONS_TO_INTEGER (uid, uid_t, id);
-  BLOCK_INPUT;
+  block_input ();
   pw = getpwuid (id);
-  UNBLOCK_INPUT;
+  unblock_input ();
   return (pw ? build_string (pw->pw_name) : Qnil);
 }
 
@@ -1265,6 +1273,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,
@@ -1286,15 +1312,15 @@ name, or nil if there is no such user.  */)
     {
       uid_t u;
       CONS_TO_INTEGER (uid, uid_t, u);
-      BLOCK_INPUT;
+      block_input ();
       pw = getpwuid (u);
-      UNBLOCK_INPUT;
+      unblock_input ();
     }
   else if (STRINGP (uid))
     {
-      BLOCK_INPUT;
+      block_input ();
       pw = getpwnam (SSDATA (uid));
-      UNBLOCK_INPUT;
+      unblock_input ();
     }
   else
     error ("Invalid UID specification");
@@ -1337,15 +1363,6 @@ DEFUN ("system-name", Fsystem_name, Ssystem_name, 0, 0, 0,
   return Vsystem_name;
 }
 
-const char *
-get_system_name (void)
-{
-  if (STRINGP (Vsystem_name))
-    return SSDATA (Vsystem_name);
-  else
-    return "";
-}
-
 DEFUN ("emacs-pid", Femacs_pid, Semacs_pid, 0, 0, 0,
        doc: /* Return the process ID of Emacs, as a number.  */)
   (void)
@@ -1475,8 +1492,8 @@ make_lisp_time (EMACS_TIME t)
 
 /* Decode a Lisp list SPECIFIED_TIME that represents a time.
    Set *PHIGH, *PLOW, *PUSEC, *PPSEC to its parts; do not check their values.
-   Return nonzero if successful.  */
-static int
+   Return true if successful.  */
+static bool
 disassemble_lisp_time (Lisp_Object specified_time, Lisp_Object *phigh,
                       Lisp_Object *plow, Lisp_Object *pusec,
                       Lisp_Object *ppsec)
@@ -1519,8 +1536,8 @@ disassemble_lisp_time (Lisp_Object specified_time, Lisp_Object *phigh,
    If *DRESULT is not null, store into *DRESULT the number of
    seconds since the start of the POSIX Epoch.
 
-   Return nonzero if successful.  */
-int
+   Return true if successful.  */
+bool
 decode_time_components (Lisp_Object high, Lisp_Object low, Lisp_Object usec,
                        Lisp_Object psec,
                        EMACS_TIME *result, double *dresult)
@@ -1640,7 +1657,7 @@ or (if you need time as a string) `format-time-string'.  */)
 
 /* Write information into buffer S of size MAXSIZE, according to the
    FORMAT of length FORMAT_LEN, using time information taken from *TP.
-   Default to Universal Time if UT is nonzero, local time otherwise.
+   Default to Universal Time if UT, local time otherwise.
    Use NS as the number of nanoseconds in the %N directive.
    Return the number of bytes written, not including the terminating
    '\0'.  If S is NULL, nothing will be written anywhere; so to
@@ -1651,7 +1668,7 @@ or (if you need time as a string) `format-time-string'.  */)
    bytes in FORMAT and it does not support nanoseconds.  */
 static size_t
 emacs_nmemftime (char *s, size_t maxsize, const char *format,
-                size_t format_len, const struct tm *tp, int ut, int ns)
+                size_t format_len, const struct tm *tp, bool ut, int ns)
 {
   size_t total = 0;
 
@@ -1756,7 +1773,7 @@ usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL)  */)
 
 static Lisp_Object
 format_time_string (char const *format, ptrdiff_t formatlen,
-                   EMACS_TIME t, int ut, struct tm *tmp)
+                   EMACS_TIME t, bool ut, struct tm *tmp)
 {
   char buffer[4000];
   char *buf = buffer;
@@ -1770,14 +1787,14 @@ format_time_string (char const *format, ptrdiff_t formatlen,
   while (1)
     {
       time_t *taddr = emacs_secs_addr (&t);
-      BLOCK_INPUT;
+      block_input ();
 
       synchronize_system_time_locale ();
 
       tm = ut ? gmtime (taddr) : localtime (taddr);
       if (! tm)
        {
-         UNBLOCK_INPUT;
+         unblock_input ();
          time_overflow ();
        }
       *tmp = *tm;
@@ -1789,14 +1806,14 @@ format_time_string (char const *format, ptrdiff_t formatlen,
 
       /* Buffer was too small, so make it bigger and try again.  */
       len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns);
-      UNBLOCK_INPUT;
+      unblock_input ();
       if (STRING_BYTES_BOUND <= len)
        string_overflow ();
       size = len + 1;
       buf = SAFE_ALLOCA (size);
     }
 
-  UNBLOCK_INPUT;
+  unblock_input ();
   bufstring = make_unibyte_string (buf, len);
   SAFE_FREE ();
   return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
@@ -1824,11 +1841,11 @@ DOW and ZONE.)  */)
   struct tm *decoded_time;
   Lisp_Object list_args[9];
 
-  BLOCK_INPUT;
+  block_input ();
   decoded_time = localtime (&time_spec);
   if (decoded_time)
     save_tm = *decoded_time;
-  UNBLOCK_INPUT;
+  unblock_input ();
   if (! (decoded_time
         && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year
         && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
@@ -1844,13 +1861,13 @@ DOW and ZONE.)  */)
   XSETFASTINT (list_args[6], save_tm.tm_wday);
   list_args[7] = save_tm.tm_isdst ? Qt : Qnil;
 
-  BLOCK_INPUT;
+  block_input ();
   decoded_time = gmtime (&time_spec);
   if (decoded_time == 0)
     list_args[8] = Qnil;
   else
     XSETINT (list_args[8], tm_diff (&save_tm, decoded_time));
-  UNBLOCK_INPUT;
+  unblock_input ();
   return Flist (9, list_args);
 }
 
@@ -1908,15 +1925,17 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
     zone = XCAR (zone);
   if (NILP (zone))
     {
-      BLOCK_INPUT;
+      block_input ();
       value = mktime (&tm);
-      UNBLOCK_INPUT;
+      unblock_input ();
     }
   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";
@@ -1928,14 +1947,21 @@ 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");
 
-      BLOCK_INPUT;
+      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
         value doesn't suffice, since that would mishandle leap seconds.  */
@@ -1943,15 +1969,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);
+      unblock_input ();
+      SAFE_FREE ();
     }
 
   if (value == (time_t) -1)
@@ -1985,7 +2008,7 @@ but this is considered obsolete.  */)
      newline, and without the 4-digit year limit.  Don't use asctime
      or ctime, as they might dump core if the year is outside the
      range -999 .. 9999.  */
-  BLOCK_INPUT;
+  block_input ();
   tm = localtime (&value);
   if (tm)
     {
@@ -2001,7 +2024,7 @@ but this is considered obsolete.  */)
                     tm->tm_hour, tm->tm_min, tm->tm_sec,
                     tm->tm_year + year_base);
     }
-  UNBLOCK_INPUT;
+  unblock_input ();
   if (! tm)
     time_overflow ();
 
@@ -2057,11 +2080,11 @@ the data it can't find.  */)
   zone_offset = Qnil;
   value = make_emacs_time (lisp_seconds_argument (specified_time), 0);
   zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &localtm);
-  BLOCK_INPUT;
+  block_input ();
   t = gmtime (emacs_secs_addr (&value));
   if (t)
     offset = tm_diff (&localtm, t);
-  UNBLOCK_INPUT;
+  unblock_input ();
 
   if (t)
     {
@@ -2081,16 +2104,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.
@@ -2103,18 +2116,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))
@@ -2122,106 +2127,97 @@ only the former.  */)
   else
     tzstring = SSDATA (tz);
 
+  block_input ();
   set_time_zone_rule (tzstring);
-  environbuf = environ;
-
-  UNBLOCK_INPUT;
+  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;
+
+  int tzeqlen = sizeof "TZ=" - 1;
 
-  /* 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));
+#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 ();
+#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;
-
-  environ = newenv;
+  if (!tzstring)
+    {
+      unsetenv ("TZ");
+      tzvalbuf_in_environ = 0;
+    }
+  else
+    {
+      ptrdiff_t tzstringlen = strlen (tzstring);
 
-  /* 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.  */
+      if (tzvalbufsize <= tzeqlen + tzstringlen)
+       {
+         unsetenv ("TZ");
+         tzvalbuf_in_environ = 0;
+         tzvalbuf = xpalloc (tzvalbuf, &tzvalbufsize,
+                             tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1);
+         memcpy (tzvalbuf, "TZ=", tzeqlen);
+       }
 
-#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;
-      }
+      strcpy (tzvalbuf + tzeqlen, tzstring);
 
-    /* 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
 }
@@ -2236,11 +2232,11 @@ general_insert_function (void (*insert_func)
                              (const char *, ptrdiff_t),
                         void (*insert_from_string_func)
                              (Lisp_Object, ptrdiff_t, ptrdiff_t,
-                              ptrdiff_t, ptrdiff_t, int),
-                        int inherit, ptrdiff_t nargs, Lisp_Object *args)
+                              ptrdiff_t, ptrdiff_t, bool),
+                        bool inherit, ptrdiff_t nargs, Lisp_Object *args)
 {
   ptrdiff_t argnum;
-  register Lisp_Object val;
+  Lisp_Object val;
 
   for (argnum = 0; argnum < nargs; argnum++)
     {
@@ -2366,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:
@@ -2463,7 +2459,7 @@ from adjoining text, if those properties are sticky.  */)
 /* Return a Lisp_String containing the text of the current buffer from
    START to END.  If text properties are in use and the current buffer
    has properties in the range specified, the resulting string will also
-   have them, if PROPS is nonzero.
+   have them, if PROPS is true.
 
    We don't want to use plain old make_string here, because it calls
    make_uninit_string, which can cause the buffer arena to be
@@ -2474,7 +2470,7 @@ from adjoining text, if those properties are sticky.  */)
    buffer substrings.  */
 
 Lisp_Object
-make_buffer_string (ptrdiff_t start, ptrdiff_t end, int props)
+make_buffer_string (ptrdiff_t start, ptrdiff_t end, bool props)
 {
   ptrdiff_t start_byte = CHAR_TO_BYTE (start);
   ptrdiff_t end_byte = CHAR_TO_BYTE (end);
@@ -2487,7 +2483,7 @@ make_buffer_string (ptrdiff_t start, ptrdiff_t end, int props)
 
    If text properties are in use and the current buffer
    has properties in the range specified, the resulting string will also
-   have them, if PROPS is nonzero.
+   have them, if PROPS is true.
 
    We don't want to use plain old make_string here, because it calls
    make_uninit_string, which can cause the buffer arena to be
@@ -2499,12 +2495,12 @@ make_buffer_string (ptrdiff_t start, ptrdiff_t end, int props)
 
 Lisp_Object
 make_buffer_string_both (ptrdiff_t start, ptrdiff_t start_byte,
-                        ptrdiff_t end, ptrdiff_t end_byte, int props)
+                        ptrdiff_t end, ptrdiff_t end_byte, bool props)
 {
   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);
@@ -2602,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,
@@ -2621,7 +2617,7 @@ They default to the values of (point-min) and (point-max) in BUFFER.  */)
   if (NILP (buf))
     nsberror (buffer);
   bp = XBUFFER (buf);
-  if (NILP (BVAR (bp, name)))
+  if (!BUFFER_LIVE_P (bp))
     error ("Selecting deleted buffer");
 
   if (NILP (start))
@@ -2657,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.  */)
@@ -2685,7 +2681,7 @@ determines whether case is significant or ignored.  */)
       if (NILP (buf1))
        nsberror (buffer1);
       bp1 = XBUFFER (buf1);
-      if (NILP (BVAR (bp1, name)))
+      if (!BUFFER_LIVE_P (bp1))
        error ("Selecting deleted buffer");
     }
 
@@ -2723,7 +2719,7 @@ determines whether case is significant or ignored.  */)
       if (NILP (buf2))
        nsberror (buffer2);
       bp2 = XBUFFER (buf2);
-      if (NILP (BVAR (bp2, name)))
+      if (!BUFFER_LIVE_P (bp2))
        error ("Selecting deleted buffer");
     }
 
@@ -2791,8 +2787,8 @@ determines whether case is significant or ignored.  */)
 
       if (!NILP (trt))
        {
-         c1 = CHAR_TABLE_TRANSLATE (trt, c1);
-         c2 = CHAR_TABLE_TRANSLATE (trt, c2);
+         c1 = char_table_translate (trt, c1);
+         c2 = char_table_translate (trt, c2);
        }
       if (c1 < c2)
        return make_number (- 1 - chars);
@@ -2816,13 +2812,15 @@ determines whether case is significant or ignored.  */)
 static Lisp_Object
 subst_char_in_region_unwind (Lisp_Object arg)
 {
-  return BVAR (current_buffer, undo_list) = arg;
+  bset_undo_list (current_buffer, arg);
+  return arg;
 }
 
 static Lisp_Object
 subst_char_in_region_unwind_1 (Lisp_Object arg)
 {
-  return BVAR (current_buffer, filename) = arg;
+  bset_filename (current_buffer, arg);
+  return arg;
 }
 
 DEFUN ("subst-char-in-region", Fsubst_char_in_region,
@@ -2848,7 +2846,8 @@ Both characters must have the same length of multi-byte form.  */)
 #define COMBINING_BOTH (COMBINING_BEFORE | COMBINING_AFTER)
   int maybe_byte_combining = COMBINING_NO;
   ptrdiff_t last_changed = 0;
-  int multibyte_p = !NILP (BVAR (current_buffer, enable_multibyte_characters));
+  bool multibyte_p
+    = !NILP (BVAR (current_buffer, enable_multibyte_characters));
   int fromc, toc;
 
  restart:
@@ -2896,11 +2895,11 @@ Both characters must have the same length of multi-byte form.  */)
     {
       record_unwind_protect (subst_char_in_region_unwind,
                             BVAR (current_buffer, undo_list));
-      BVAR (current_buffer, undo_list) = Qt;
+      bset_undo_list (current_buffer, Qt);
       /* Don't do file-locking.  */
       record_unwind_protect (subst_char_in_region_unwind_1,
                             BVAR (current_buffer, filename));
-      BVAR (current_buffer, filename) = Qnil;
+      bset_filename (current_buffer, Qnil);
     }
 
   if (pos_byte < GPT_BYTE)
@@ -2933,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))
                {
@@ -2982,7 +2981,7 @@ Both characters must have the same length of multi-byte form.  */)
                INC_POS (pos_byte_next);
 
              if (! NILP (noundo))
-               BVAR (current_buffer, undo_list) = tem;
+               bset_undo_list (current_buffer, tem);
 
              UNGCPRO;
            }
@@ -3084,8 +3083,8 @@ It returns the number of characters changed.  */)
   int cnt;                     /* Number of changes made. */
   ptrdiff_t size;              /* Size of translate table. */
   ptrdiff_t pos, pos_byte, end_pos;
-  int multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters));
-  int string_multibyte IF_LINT (= 0);
+  bool multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters));
+  bool string_multibyte IF_LINT (= 0);
 
   validate_region (&start, &end);
   if (CHAR_TABLE_P (table))
@@ -3109,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; )
@@ -3438,8 +3437,8 @@ 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
@@ -3615,9 +3614,13 @@ where flags is [+ #-0]+, width is [0-9]+, and precision is .[0-9]+
 The + flag character inserts a + before any positive number, while a
 space inserts a space before any positive number; these flags only
 affect %d, %e, %f, and %g sequences, and the + flag takes precedence.
+The - and 0 flags affect the width specifier, as described below.
+
 The # flag means to use an alternate display form for %o, %x, %X, %e,
-%f, and %g sequences.  The - and 0 flags affect the width specifier,
-as described below.
+%f, and %g sequences: for %o, it ensures that the result begins with
+\"0\"; for %x and %X, it prefixes the result with \"0x\" or \"0X\";
+for %e, %f, and %g, it causes a decimal point to be included even if
+the precision is zero.
 
 The width specifier supplies a lower limit for the length of the
 printed representation.  The padding, if any, normally goes on the
@@ -3641,20 +3644,20 @@ usage: (format STRING &rest OBJECTS)  */)
   ptrdiff_t max_bufsize = STRING_BYTES_BOUND + 1;
   char *p;
   Lisp_Object buf_save_value IF_LINT (= {0});
-  register char *format, *end, *format_start;
+  char *format, *end, *format_start;
   ptrdiff_t formatlen, nchars;
-  /* Nonzero if the format is multibyte.  */
-  int multibyte_format = 0;
-  /* Nonzero if the output should be a multibyte string,
+  /* True if the format is multibyte.  */
+  bool multibyte_format = 0;
+  /* True if the output should be a multibyte string,
      which is true if any of the inputs is one.  */
-  int multibyte = 0;
+  bool multibyte = 0;
   /* When we make a multibyte string, we must pay attention to the
      byte combining problem, i.e., a byte may be combined with a
      multibyte character of the previous string.  This flag tells if we
      must consider such a situation or not.  */
-  int maybe_combine_byte;
+  bool maybe_combine_byte;
   Lisp_Object val;
-  int arg_intervals = 0;
+  bool arg_intervals = 0;
   USE_SAFE_ALLOCA;
 
   /* discarded[I] is 1 if byte I of the format
@@ -3670,8 +3673,8 @@ usage: (format STRING &rest OBJECTS)  */)
   struct info
   {
     ptrdiff_t start, end;
-    int converted_to_string;
-    int intervals;
+    unsigned converted_to_string : 1;
+    unsigned intervals : 1;
   } *info = 0;
 
   /* It should not be necessary to GCPRO ARGS, because
@@ -3748,13 +3751,13 @@ usage: (format STRING &rest OBJECTS)  */)
             digits to print after the '.' for floats, or the max.
             number of chars to print from a string.  */
 
-         int minus_flag = 0;
-         int  plus_flag = 0;
-         int space_flag = 0;
-         int sharp_flag = 0;
-         int  zero_flag = 0;
+         bool minus_flag = 0;
+         bool  plus_flag = 0;
+         bool space_flag = 0;
+         bool sharp_flag = 0;
+         bool  zero_flag = 0;
          ptrdiff_t field_width;
-         int precision_given;
+         bool precision_given;
          uintmax_t precision = UINTMAX_MAX;
          char *num_end;
          char conversion;
@@ -3933,7 +3936,7 @@ usage: (format STRING &rest OBJECTS)  */)
 
                  /* If this argument has text properties, record where
                     in the result string it appears.  */
-                 if (STRING_INTERVALS (args[n]))
+                 if (string_intervals (args[n]))
                    info[n].intervals = arg_intervals = 1;
 
                  continue;
@@ -4134,7 +4137,7 @@ usage: (format STRING &rest OBJECTS)  */)
                   char *src = sprintf_buf;
                  char src0 = src[0];
                  int exponent_bytes = 0;
-                 int signedp = src0 == '-' || src0 == '+' || src0 == ' ';
+                 bool signedp = src0 == '-' || src0 == '+' || src0 == ' ';
                  int significand_bytes;
                  if (zero_flag
                      && ((src[signedp] >= '0' && src[signedp] <= '9')
@@ -4264,7 +4267,7 @@ usage: (format STRING &rest OBJECTS)  */)
     }
 
   if (bufsize < p - buf)
-    abort ();
+    emacs_abort ();
 
   if (maybe_combine_byte)
     nchars = multibyte_chars_in_text ((unsigned char *) buf, p - buf);
@@ -4277,7 +4280,7 @@ usage: (format STRING &rest OBJECTS)  */)
      arguments has text properties, set up text properties of the
      result string.  */
 
-  if (STRING_INTERVALS (args[0]) || arg_intervals)
+  if (string_intervals (args[0]) || arg_intervals)
     {
       Lisp_Object len, new_len, props;
       struct gcpro gcpro1;
@@ -4527,7 +4530,7 @@ Transposing beyond buffer boundaries is an error.  */)
   Lisp_Object buf;
 
   XSETBUFFER (buf, current_buffer);
-  cur_intv = BUF_INTERVALS (current_buffer);
+  cur_intv = buffer_intervals (current_buffer);
 
   validate_region (&startr1, &endr1);
   validate_region (&startr2, &endr2);
@@ -4604,7 +4607,7 @@ Transposing beyond buffer boundaries is an error.  */)
                                     len1_byte, end2, start2_byte + len2_byte)
          || count_combining_after (BYTE_POS_ADDR (start1_byte),
                                    len1_byte, end2, start2_byte + len2_byte))
-       abort ();
+       emacs_abort ();
     }
   else
     {
@@ -4616,7 +4619,7 @@ Transposing beyond buffer boundaries is an error.  */)
                                    len2_byte, end1, start1_byte + len1_byte)
          || count_combining_after (BYTE_POS_ADDR (start1_byte),
                                    len1_byte, end2, start2_byte + len2_byte))
-       abort ();
+       emacs_abort ();
     }
 #endif
 
@@ -4629,7 +4632,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);
@@ -4637,7 +4640,7 @@ Transposing beyond buffer boundaries is an error.  */)
       /* Don't use Fset_text_properties: that can cause GC, which can
         clobber objects stored in the tmp_intervals.  */
       tmp_interval3 = validate_interval_range (buf, &startr1, &endr2, 0);
-      if (!NULL_INTERVAL_P (tmp_interval3))
+      if (tmp_interval3)
        set_text_properties_1 (startr1, endr2, Qnil, buf, tmp_interval3);
 
       /* First region smaller than second.  */
@@ -4688,19 +4691,19 @@ 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);
           tmp_interval2 = copy_intervals (cur_intv, start2, len2);
 
          tmp_interval3 = validate_interval_range (buf, &startr1, &endr1, 0);
-         if (!NULL_INTERVAL_P (tmp_interval3))
+         if (tmp_interval3)
            set_text_properties_1 (startr1, endr1, Qnil, buf, tmp_interval3);
 
          tmp_interval3 = validate_interval_range (buf, &startr2, &endr2, 0);
-         if (!NULL_INTERVAL_P (tmp_interval3))
+         if (tmp_interval3)
            set_text_properties_1 (startr2, endr2, Qnil, buf, tmp_interval3);
 
          temp = SAFE_ALLOCA (len1_byte);
@@ -4722,14 +4725,14 @@ 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);
           tmp_interval2 = copy_intervals (cur_intv, start2, len2);
 
          tmp_interval3 = validate_interval_range (buf, &startr1, &endr2, 0);
-         if (!NULL_INTERVAL_P (tmp_interval3))
+         if (tmp_interval3)
            set_text_properties_1 (startr1, endr2, Qnil, buf, tmp_interval3);
 
          /* holds region 2 */
@@ -4755,14 +4758,14 @@ 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);
           tmp_interval2 = copy_intervals (cur_intv, start2, len2);
 
          tmp_interval3 = validate_interval_range (buf, &startr1, &endr2, 0);
-         if (!NULL_INTERVAL_P (tmp_interval3))
+         if (tmp_interval3)
            set_text_properties_1 (startr1, endr2, Qnil, buf, tmp_interval3);
 
          /* holds region 1 */
@@ -4806,9 +4809,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,
@@ -4917,6 +4917,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);