]> code.delx.au - gnu-emacs/blobdiff - src/editfns.c
Time zone name fixes for non-ASCII locales (Bug#641, Bug#9794)
[gnu-emacs] / src / editfns.c
index 577263c5aea1aa804d46b2e06f1ae181fcfe57a7..83cd4bd55359358fad7339a93d873bff68ded493 100644 (file)
@@ -85,6 +85,8 @@ extern Lisp_Object w32_get_internal_run_time (void);
 #endif
 
 static void time_overflow (void) NO_RETURN;
+static Lisp_Object format_time_string (char const *, ptrdiff_t, Lisp_Object,
+                                      int, time_t *, struct tm **);
 static int tm_diff (struct tm *, struct tm *);
 static void update_buffer_properties (EMACS_INT, EMACS_INT);
 
@@ -146,7 +148,7 @@ init_editfns (void)
   /* If the user name claimed in the environment vars differs from
      the real uid, use the claimed name to find the full name.  */
   tem = Fstring_equal (Vuser_login_name, Vuser_real_login_name);
-  Vuser_full_name = Fuser_full_name (NILP (tem)? make_number (geteuid())
+  Vuser_full_name = Fuser_full_name (NILP (tem)? make_number (geteuid ())
                                     : Vuser_login_name);
 
   p = getenv ("NAME");
@@ -1695,36 +1697,46 @@ The modifiers are `E' and `O'.  For certain characters X,
 %EX is a locale's alternative version of %X;
 %OX is like %X, but uses the locale's number symbols.
 
-For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
+For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".
+
+usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL)  */)
   (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object universal)
 {
-  time_t value;
+  time_t t;
+  struct tm *tm;
+
+  CHECK_STRING (format_string);
+  format_string = code_convert_string_norecord (format_string,
+                                               Vlocale_coding_system, 1);
+  return format_time_string (SSDATA (format_string), SBYTES (format_string),
+                            timeval, ! NILP (universal), &t, &tm);
+}
+
+static Lisp_Object
+format_time_string (char const *format, ptrdiff_t formatlen,
+                   Lisp_Object timeval, int ut, time_t *tval, struct tm **tmp)
+{
   ptrdiff_t size;
   int usec;
   int ns;
   struct tm *tm;
-  int ut = ! NILP (universal);
-
-  CHECK_STRING (format_string);
 
-  if (! (lisp_time_argument (timeval, &value, &usec)
+  if (! (lisp_time_argument (timeval, tval, &usec)
         && 0 <= usec && usec < 1000000))
     error ("Invalid time specification");
   ns = usec * 1000;
 
-  format_string = code_convert_string_norecord (format_string,
-                                               Vlocale_coding_system, 1);
-
   /* This is probably enough.  */
-  size = SBYTES (format_string);
+  size = formatlen;
   if (size <= (STRING_BYTES_BOUND - 50) / 6)
     size = size * 6 + 50;
 
   BLOCK_INPUT;
-  tm = ut ? gmtime (&value) : localtime (&value);
+  tm = ut ? gmtime (tval) : localtime (tval);
   UNBLOCK_INPUT;
   if (! tm)
     time_overflow ();
+  *tmp = tm;
 
   synchronize_system_time_locale ();
 
@@ -1735,9 +1747,7 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
 
       buf[0] = '\1';
       BLOCK_INPUT;
-      result = emacs_nmemftime (buf, size, SSDATA (format_string),
-                               SBYTES (format_string),
-                               tm, ut, ns);
+      result = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns);
       UNBLOCK_INPUT;
       if ((result > 0 && result < size) || (result == 0 && buf[0] == '\0'))
        return code_convert_string_norecord (make_unibyte_string (buf, result),
@@ -1745,9 +1755,7 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
 
       /* If buffer was too small, make it bigger and try again.  */
       BLOCK_INPUT;
-      result = emacs_nmemftime (NULL, (size_t) -1,
-                               SSDATA (format_string),
-                               SBYTES (format_string),
+      result = emacs_nmemftime (NULL, (size_t) -1, format, formatlen,
                                tm, ut, ns);
       UNBLOCK_INPUT;
       if (STRING_BYTES_BOUND <= result)
@@ -1992,50 +2000,34 @@ the data it can't find.  */)
 {
   time_t value;
   struct tm *t;
-  struct tm gmt;
-
-  if (!lisp_time_argument (specified_time, &value, NULL))
-    t = NULL;
-  else
-    {
-      BLOCK_INPUT;
-      t = gmtime (&value);
-      if (t)
-       {
-         gmt = *t;
-         t = localtime (&value);
-       }
-      UNBLOCK_INPUT;
-    }
+  struct tm localtm;
+  struct tm *localt;
+  Lisp_Object zone_offset, zone_name;
+
+  zone_offset = Qnil;
+  zone_name = format_time_string ("%Z", sizeof "%Z" - 1, specified_time,
+                                 0, &value, &localt);
+  localtm = *localt;
+  BLOCK_INPUT;
+  t = gmtime (&value);
+  UNBLOCK_INPUT;
 
   if (t)
     {
-      int offset = tm_diff (t, &gmt);
-      char *s = 0;
-      char buf[6];
-
-#ifdef HAVE_TM_ZONE
-      if (t->tm_zone)
-       s = (char *)t->tm_zone;
-#else /* not HAVE_TM_ZONE */
-#ifdef HAVE_TZNAME
-      if (t->tm_isdst == 0 || t->tm_isdst == 1)
-       s = tzname[t->tm_isdst];
-#endif
-#endif /* not HAVE_TM_ZONE */
-
-      if (!s)
+      int offset = tm_diff (&localtm, t);
+      zone_offset = make_number (offset);
+      if (SCHARS (zone_name) == 0)
        {
          /* No local time zone name is available; use "+-NNNN" instead.  */
-         int am = (offset < 0 ? -offset : offset) / 60;
+         int m = offset / 60;
+         int am = offset < 0 ? - m : m;
+         char buf[sizeof "+00" + INT_STRLEN_BOUND (int)];
          sprintf (buf, "%c%02d%02d", (offset < 0 ? '-' : '+'), am/60, am%60);
-         s = buf;
+         zone_name = build_string (buf);
        }
-
-      return Fcons (make_number (offset), Fcons (build_string (s), Qnil));
     }
-  else
-    return Fmake_list (make_number (2), Qnil);
+
+  return list2 (zone_offset, zone_name);
 }
 
 /* This holds the value of `environ' produced by the previous
@@ -2051,7 +2043,12 @@ 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.
-If TZ is t, use Universal Time.  */)
+If TZ is t, use Universal Time.
+
+Instead of calling this function, you typically want (setenv "TZ" TZ).
+That changes both the environment of the Emacs process and the
+variable `process-environment', whereas `set-time-zone-rule' affects
+only the former.  */)
   (Lisp_Object tz)
 {
   const char *tzstring;
@@ -2071,7 +2068,7 @@ If TZ is t, use Universal Time.  */)
     }
 
   set_time_zone_rule (tzstring);
-  free (environbuf);
+  xfree (environbuf);
   environbuf = environ;
 
   return Qnil;
@@ -2100,7 +2097,7 @@ static char set_time_zone_rule_tz2[] = "TZ=GMT+1";
 void
 set_time_zone_rule (const char *tzstring)
 {
-  int envptrs;
+  ptrdiff_t envptrs;
   char **from, **to, **newenv;
 
   /* Make the ENVIRON vector longer with room for TZSTRING.  */
@@ -3246,7 +3243,7 @@ save_restriction_save (void)
       end = buildmark (ZV, ZV_BYTE);
 
       /* END must move forward if text is inserted at its exact location.  */
-      XMARKER(end)->insertion_type = 1;
+      XMARKER (end)->insertion_type = 1;
 
       return Fcons (beg, end);
     }
@@ -3350,7 +3347,7 @@ usage: (save-restriction &rest BODY)  */)
 static char *message_text;
 
 /* Allocated length of that buffer.  */
-static int message_length;
+static ptrdiff_t message_length;
 
 DEFUN ("message", Fmessage, Smessage, 1, MANY, 0,
        doc: /* Display a message at the bottom of the screen.
@@ -3432,8 +3429,8 @@ usage: (message-box FORMAT-STRING &rest ARGS)  */)
        }
       if (SBYTES (val) > message_length)
        {
+         message_text = (char *) xrealloc (message_text, SBYTES (val));
          message_length = SBYTES (val);
-         message_text = (char *)xrealloc (message_text, message_length);
        }
       memcpy (message_text, SDATA (val), SBYTES (val));
       message2 (message_text, SBYTES (val),
@@ -3882,7 +3879,7 @@ usage: (format STRING &rest OBJECTS)  */)
                      : -1)),
 
                /* Maximum number of bytes generated by any format, if
-                  precision is no more than DBL_USEFUL_PRECISION_MAX.
+                  precision is no more than USEFUL_PRECISION_MAX.
                   On all practical hosts, %f is the worst case.  */
                SPRINTF_BUFSIZE =
                  sizeof "-." + (DBL_MAX_10_EXP + 1) + USEFUL_PRECISION_MAX,
@@ -4129,8 +4126,8 @@ usage: (format STRING &rest OBJECTS)  */)
                format++;
              while (! CHAR_HEAD_P (*format));
 
-             convbytes = format - format0;
-             memset (&discarded[format0 + 1 - format_start], 2, convbytes - 1);
+             convbytes = format - src;
+             memset (&discarded[src + 1 - format_start], 2, convbytes - 1);
            }
          else
            {
@@ -4158,7 +4155,7 @@ usage: (format STRING &rest OBJECTS)  */)
         character.  CONVBYTES says how much room is needed.  Allocate
         enough room (and then some) and do it again.  */
       {
-       EMACS_INT used = p - buf;
+       ptrdiff_t used = p - buf;
 
        if (max_bufsize - used < convbytes)
          string_overflow ();