* lib/getopt_.h: Remove trailing CRs that crept in.
[bpt/emacs.git] / src / editfns.c
index 65baaf4..64269ba 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.
 
@@ -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>
@@ -79,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)
@@ -97,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
@@ -739,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)
@@ -783,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,
@@ -812,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)
 {
-  bool visible = (XBUFFER (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;
-  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 (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);
@@ -847,35 +870,24 @@ 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 (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);
   bset_mark_active (current_buffer, tem);
 
@@ -899,8 +911,8 @@ 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 = XWINDOW (tem)->buffer,
          (/* Window is live...  */
@@ -910,6 +922,10 @@ save_excursion_restore (Lisp_Object info)
     Fset_window_point (tem, make_number (PT));
 
   UNGCPRO;
+
+ out:
+
+  free_save_value (info);
   return Qnil;
 }
 
@@ -952,7 +968,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)
@@ -1218,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);
 }
 
@@ -1257,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,
@@ -1278,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");
@@ -1329,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)
@@ -1762,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;
@@ -1781,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);
@@ -1816,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))
@@ -1836,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);
 }
 
@@ -1900,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";
@@ -1920,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.  */
@@ -1935,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)
@@ -1977,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)
     {
@@ -1993,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 ();
 
@@ -2049,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)
     {
@@ -2073,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.
@@ -2095,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))
@@ -2114,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
 }
@@ -2358,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:
@@ -2496,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);
@@ -2594,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,
@@ -2613,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))
@@ -2649,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.  */)
@@ -2677,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");
     }
 
@@ -2715,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");
     }
 
@@ -2928,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))
                {
@@ -3104,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; )
@@ -3433,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
@@ -4518,7 +4522,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;
 
@@ -4579,20 +4583,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)
@@ -4628,7 +4634,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);
@@ -4687,8 +4693,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);
@@ -4721,7 +4727,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);
@@ -4754,7 +4760,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);
@@ -4805,9 +4811,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,
@@ -4882,12 +4885,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);
@@ -4916,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);