Assume C89 or later.
[bpt/emacs.git] / src / editfns.c
index e3a7d1f..c5ba280 100644 (file)
@@ -1,6 +1,6 @@
 /* Lisp functions pertaining to editing.
 
-Copyright (C) 1985-1987, 1989, 1993-2011 Free Software Foundation, Inc.
+Copyright (C) 1985-1987, 1989, 1993-2012 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -59,10 +59,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "window.h"
 #include "blockinput.h"
 
-#ifndef NULL
-#define NULL 0
-#endif
-
 #ifndef USER_FULL_NAME
 #define USER_FULL_NAME pw->pw_gecos
 #endif
@@ -73,18 +69,13 @@ extern char **environ;
 
 #define TM_YEAR_BASE 1900
 
-/* Nonzero if TM_YEAR is a struct tm's tm_year value that causes
-   asctime to have well-defined behavior.  */
-#ifndef TM_YEAR_IN_ASCTIME_RANGE
-# define TM_YEAR_IN_ASCTIME_RANGE(tm_year) \
-    (1000 - TM_YEAR_BASE <= (tm_year) && (tm_year) <= 9999 - TM_YEAR_BASE)
-#endif
-
 #ifdef WINDOWSNT
 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 +137,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");
@@ -194,8 +185,7 @@ DEFUN ("byte-to-string", Fbyte_to_string, Sbyte_to_string, 1, 1, 0,
 }
 
 DEFUN ("string-to-char", Fstring_to_char, Sstring_to_char, 1, 1, 0,
-       doc: /* Convert arg STRING to a character, the first character of that string.
-A multibyte character is handled correctly.  */)
+       doc: /* Return the first character in STRING.  */)
   (register Lisp_Object string)
 {
   register Lisp_Object val;
@@ -662,10 +652,11 @@ is after LIMIT, then LIMIT will be returned instead.  */)
 
 DEFUN ("constrain-to-field", Fconstrain_to_field, Sconstrain_to_field, 2, 5, 0,
        doc: /* Return the position closest to NEW-POS that is in the same field as OLD-POS.
-
 A field is a region of text with the same `field' property.
-If NEW-POS is nil, then the current point is used instead, and set to the
-constrained position if that is different.
+
+If NEW-POS is nil, then use the current point instead, and move point
+to the resulting constrained position, in addition to returning that
+position.
 
 If OLD-POS is at the boundary of two fields, then the allowable
 positions for NEW-POS depends on the value of the optional argument
@@ -1696,65 +1687,72 @@ 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;
-  ptrdiff_t size;
+  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)
+{
+  char buffer[4000];
+  char *buf = buffer;
+  size_t size = sizeof buffer;
+  size_t len;
+  Lisp_Object bufstring;
   int usec;
   int ns;
   struct tm *tm;
-  int ut = ! NILP (universal);
-
-  CHECK_STRING (format_string);
+  USE_SAFE_ALLOCA;
 
-  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);
-  if (size <= (STRING_BYTES_BOUND - 50) / 6)
-    size = size * 6 + 50;
-
-  BLOCK_INPUT;
-  tm = ut ? gmtime (&value) : localtime (&value);
-  UNBLOCK_INPUT;
-  if (! tm)
-    time_overflow ();
-
-  synchronize_system_time_locale ();
-
   while (1)
     {
-      char *buf = (char *) alloca (size + 1);
-      size_t result;
+      BLOCK_INPUT;
+
+      synchronize_system_time_locale ();
+
+      tm = ut ? gmtime (tval) : localtime (tval);
+      if (! tm)
+       {
+         UNBLOCK_INPUT;
+         time_overflow ();
+       }
+      *tmp = *tm;
 
       buf[0] = '\1';
-      BLOCK_INPUT;
-      result = emacs_nmemftime (buf, size, SSDATA (format_string),
-                               SBYTES (format_string),
-                               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),
-                                            Vlocale_coding_system, 0);
+      len = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns);
+      if ((0 < len && len < size) || (len == 0 && buf[0] == '\0'))
+       break;
 
-      /* 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),
-                               tm, ut, ns);
+      /* Buffer was too small, so make it bigger and try again.  */
+      len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns);
       UNBLOCK_INPUT;
-      if (STRING_BYTES_BOUND <= result)
+      if (STRING_BYTES_BOUND <= len)
        string_overflow ();
-      size = result + 1;
+      size = len + 1;
+      SAFE_ALLOCA (buf, char *, size);
     }
+
+  UNBLOCK_INPUT;
+  bufstring = make_unibyte_string (buf, len);
+  SAFE_FREE ();
+  return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
 }
 
 DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 1, 0,
@@ -1784,31 +1782,32 @@ DOW and ZONE.)  */)
 
   BLOCK_INPUT;
   decoded_time = localtime (&time_spec);
+  /* Make a copy, in case a signal handler modifies TZ or the struct.  */
+  if (decoded_time)
+    save_tm = *decoded_time;
   UNBLOCK_INPUT;
   if (! (decoded_time
-        && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= decoded_time->tm_year
-        && decoded_time->tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
+        && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year
+        && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
     time_overflow ();
-  XSETFASTINT (list_args[0], decoded_time->tm_sec);
-  XSETFASTINT (list_args[1], decoded_time->tm_min);
-  XSETFASTINT (list_args[2], decoded_time->tm_hour);
-  XSETFASTINT (list_args[3], decoded_time->tm_mday);
-  XSETFASTINT (list_args[4], decoded_time->tm_mon + 1);
+  XSETFASTINT (list_args[0], save_tm.tm_sec);
+  XSETFASTINT (list_args[1], save_tm.tm_min);
+  XSETFASTINT (list_args[2], save_tm.tm_hour);
+  XSETFASTINT (list_args[3], save_tm.tm_mday);
+  XSETFASTINT (list_args[4], save_tm.tm_mon + 1);
   /* On 64-bit machines an int is narrower than EMACS_INT, thus the
      cast below avoids overflow in int arithmetics.  */
-  XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) decoded_time->tm_year);
-  XSETFASTINT (list_args[6], decoded_time->tm_wday);
-  list_args[7] = (decoded_time->tm_isdst)? Qt : Qnil;
+  XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) save_tm.tm_year);
+  XSETFASTINT (list_args[6], save_tm.tm_wday);
+  list_args[7] = save_tm.tm_isdst ? Qt : Qnil;
 
-  /* Make a copy, in case gmtime modifies the struct.  */
-  save_tm = *decoded_time;
   BLOCK_INPUT;
   decoded_time = gmtime (&time_spec);
-  UNBLOCK_INPUT;
   if (decoded_time == 0)
     list_args[8] = Qnil;
   else
     XSETINT (list_args[8], tm_diff (&save_tm, decoded_time));
+  UNBLOCK_INPUT;
   return Flist (9, list_args);
 }
 
@@ -1890,21 +1889,23 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
       else
        error ("Invalid time zone specification");
 
+      BLOCK_INPUT;
+
       /* Set TZ before calling mktime; merely adjusting mktime's returned
         value doesn't suffice, since that would mishandle leap seconds.  */
       set_time_zone_rule (tzstring);
 
-      BLOCK_INPUT;
       value = mktime (&tm);
-      UNBLOCK_INPUT;
 
       /* Restore TZ to previous value.  */
       newenv = environ;
       environ = oldenv;
-      xfree (newenv);
 #ifdef LOCALTIME_CACHE
       tzset ();
 #endif
+      UNBLOCK_INPUT;
+
+      xfree (newenv);
     }
 
   if (value == (time_t) -1)
@@ -1931,24 +1932,37 @@ but this is considered obsolete.  */)
 {
   time_t value;
   struct tm *tm;
-  register char *tem;
+  char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1];
+  int len IF_LINT (= 0);
 
   if (! lisp_time_argument (specified_time, &value, NULL))
     error ("Invalid time specification");
 
-  /* Convert to a string, checking for out-of-range time stamps.
-     Don't use 'ctime', as that might dump core if VALUE is out of
-     range.  */
+  /* Convert to a string in ctime format, except without the trailing
+     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;
   tm = localtime (&value);
+  if (tm)
+    {
+      static char const wday_name[][4] =
+       { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+      static char const mon_name[][4] =
+       { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+      printmax_t year_base = TM_YEAR_BASE;
+
+      len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd,
+                    wday_name[tm->tm_wday], mon_name[tm->tm_mon], tm->tm_mday,
+                    tm->tm_hour, tm->tm_min, tm->tm_sec,
+                    tm->tm_year + year_base);
+    }
   UNBLOCK_INPUT;
-  if (! (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year) && (tem = asctime (tm))))
+  if (! tm)
     time_overflow ();
 
-  /* Remove the trailing newline.  */
-  tem[strlen (tem) - 1] = '\0';
-
-  return build_string (tem);
+  return make_unibyte_string (buf, len);
 }
 
 /* Yield A - B, measured in seconds.
@@ -1992,51 +2006,35 @@ the data it can't find.  */)
   (Lisp_Object specified_time)
 {
   time_t value;
+  int offset;
   struct tm *t;
-  struct tm gmt;
+  struct tm localtm;
+  Lisp_Object zone_offset, zone_name;
 
-  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;
-    }
+  zone_offset = Qnil;
+  zone_name = format_time_string ("%Z", sizeof "%Z" - 1, specified_time,
+                                 0, &value, &localtm);
+  BLOCK_INPUT;
+  t = gmtime (&value);
+  if (t)
+    offset = tm_diff (&localtm, t);
+  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)
+      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
@@ -2052,13 +2050,25 @@ 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;
+  char **old_environbuf;
+
+  if (! (NILP (tz) || EQ (tz, Qt)))
+    CHECK_STRING (tz);
+
+  BLOCK_INPUT;
 
   /* When called for the first time, save the original TZ.  */
-  if (!environbuf)
+  old_environbuf = environbuf;
+  if (!old_environbuf)
     initial_tz = (char *) getenv ("TZ");
 
   if (NILP (tz))
@@ -2066,15 +2076,14 @@ If TZ is t, use Universal Time.  */)
   else if (EQ (tz, Qt))
     tzstring = "UTC0";
   else
-    {
-      CHECK_STRING (tz);
-      tzstring = SSDATA (tz);
-    }
+    tzstring = SSDATA (tz);
 
   set_time_zone_rule (tzstring);
-  free (environbuf);
   environbuf = environ;
 
+  UNBLOCK_INPUT;
+
+  xfree (old_environbuf);
   return Qnil;
 }
 
@@ -2101,7 +2110,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.  */
@@ -3156,10 +3165,9 @@ It returns the number of characters changed.  */)
 }
 
 DEFUN ("delete-region", Fdelete_region, Sdelete_region, 2, 2, "r",
-       doc: /* Delete the text between point and mark.
-
-When called from a program, expects two arguments,
-positions (integers or markers) specifying the stretch to be deleted.  */)
+       doc: /* Delete the text between START and END.
+If called interactively, delete the region between point and mark.
+This command deletes buffer text without modifying the kill ring.  */)
   (Lisp_Object start, Lisp_Object end)
 {
   validate_region (&start, &end);
@@ -3248,7 +3256,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);
     }
@@ -3352,7 +3360,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.
@@ -3434,8 +3442,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),
@@ -3875,7 +3883,7 @@ usage: (format STRING &rest OBJECTS)  */)
              enum
              {
                /* Maximum precision for a %f conversion such that the
-                  trailing output digit might be nonzero.  Any precisions
+                  trailing output digit might be nonzero.  Any precision
                   larger than this will not yield useful information.  */
                USEFUL_PRECISION_MAX =
                  ((1 - DBL_MIN_EXP)
@@ -3884,7 +3892,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,
@@ -4131,8 +4139,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
            {
@@ -4160,7 +4168,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 ();