(Fsave_restriction): Doc fix.
[bpt/emacs.git] / src / editfns.c
index f31c7f6..f1f11cf 100644 (file)
@@ -847,6 +847,58 @@ lisp_time_argument (specified_time, result)
     }
 }
 
+/* Write information into buffer S of size MAXSIZE, according to the
+   FORMAT of length FORMAT_LEN, using time information taken from *TP.
+   Return the number of bytes written, not including the terminating
+   '\0'.  If S is NULL, nothing will be written anywhere; so to
+   determine how many bytes would be written, use NULL for S and
+   ((size_t) -1) for MAXSIZE.
+
+   This function behaves like emacs_strftime, except it allows null
+   bytes in FORMAT.  */
+static size_t
+emacs_memftime (s, maxsize, format, format_len, tp)
+      char *s;
+      size_t maxsize;
+      const char *format;
+      size_t format_len;
+      const struct tm *tp;
+{
+  size_t total = 0;
+
+  /* Loop through all the null-terminated strings in the format
+     argument.  Normally there's just one null-terminated string, but
+     there can be arbitrarily many, concatenated together, if the
+     format contains '\0' bytes.  emacs_strftime stops at the first
+     '\0' byte so we must invoke it separately for each such string.  */
+  for (;;)
+    {
+      size_t len;
+      size_t result;
+
+      if (s)
+       s[0] = '\1';
+
+      result = emacs_strftime (s, maxsize, format, tp);
+
+      if (s)
+       {
+         if (result == 0 && s[0] != '\0')
+           return 0;
+         s += result + 1;
+       }
+
+      maxsize -= result + 1;
+      total += result;
+      len = strlen (format);
+      if (len == format_len)
+       return total;
+      total++;
+      format += len + 1;
+      format_len -= len + 1;
+    }
+}
+
 /*
 DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
   "Use FORMAT-STRING to format the time TIME, or now if omitted.\n\
@@ -905,6 +957,7 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
 {
   time_t value;
   int size;
+  struct tm *tm;
 
   CHECK_STRING (format_string, 1);
 
@@ -914,22 +967,27 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
   /* This is probably enough.  */
   size = STRING_BYTES (XSTRING (format_string)) * 6 + 50;
 
+  tm = NILP (universal) ? localtime (&value) : gmtime (&value);
+  if (! tm)
+    error ("Specified time is not representable");
+
   while (1)
     {
       char *buf = (char *) alloca (size + 1);
       int result;
 
       buf[0] = '\1';
-      result = emacs_strftime (buf, size, XSTRING (format_string)->data,
-                              (NILP (universal) ? localtime (&value)
-                               : gmtime (&value)));
+      result = emacs_memftime (buf, size, XSTRING (format_string)->data,
+                              STRING_BYTES (XSTRING (format_string)),
+                              tm);
       if ((result > 0 && result < size) || (result == 0 && buf[0] == '\0'))
-       return build_string (buf);
+       return make_string (buf, result);
 
       /* If buffer was too small, make it bigger and try again.  */
-      result = emacs_strftime (NULL, 0x7fffffff, XSTRING (format_string)->data,
-                              (NILP (universal) ? localtime (&value)
-                               : gmtime (&value)));
+      result = emacs_memftime (NULL, (size_t) -1,
+                              XSTRING (format_string)->data,
+                              STRING_BYTES (XSTRING (format_string)),
+                              tm);
       size = result + 1;
     }
 }
@@ -959,6 +1017,8 @@ ZONE is an integer indicating the number of seconds east of Greenwich.\n\
     error ("Invalid time specification");
 
   decoded_time = localtime (&time_spec);
+  if (! decoded_time)
+    error ("Specified time is not representable");
   XSETFASTINT (list_args[0], decoded_time->tm_sec);
   XSETFASTINT (list_args[1], decoded_time->tm_min);
   XSETFASTINT (list_args[2], decoded_time->tm_hour);
@@ -1143,18 +1203,15 @@ the data it can't find.")
 {
   time_t value;
   struct tm *t;
+  struct tm gmt;
 
   if (lisp_time_argument (specified_time, &value)
-      && (t = gmtime (&value)) != 0)
+      && (t = gmtime (&value)) != 0
+      && (gmt = *t, t = localtime (&value)) != 0)
     {
-      struct tm gmt;
-      int offset;
-      char *s, buf[6];
-
-      gmt = *t;                /* Make a copy, in case localtime modifies *t.  */
-      t = localtime (&value);
-      offset = tm_diff (t, &gmt);
-      s = 0;
+      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;
@@ -1976,7 +2033,7 @@ Both characters must have the same length of multi-byte form.")
              /* replace_range is less efficient, because it moves the gap,
                 but it handles combining correctly.  */
              replace_range (pos, pos + 1, string,
-                            0, 0, 0);
+                            0, 0, 1);
              if (! NILP (noundo))
                current_buffer->undo_list = tem;
 
@@ -2212,6 +2269,7 @@ The value returned is the value of the last form in BODY.\n\
 \n\
 `save-restriction' can get confused if, within the BODY, you widen\n\
 and then make changes outside the area within the saved restrictions.\n\
+See Info node `(elisp)Narrowing' for details and an appropriate technique.\n\
 \n\
 Note: if you are using both `save-excursion' and `save-restriction',\n\
 use `save-excursion' outermost:\n\
@@ -2447,6 +2505,8 @@ Use %% to put a single % into the output.")
        if (format - this_format_start + 1 > longest_format)
          longest_format = format - this_format_start + 1;
 
+       if (format == end)
+         error ("Format string ends in middle of format specifier");
        if (*format == '%')
          format++;
        else if (++n >= nargs)
@@ -2478,7 +2538,7 @@ Use %% to put a single % into the output.")
          {
          string:
            if (*format != 's' && *format != 'S')
-             error ("format specifier doesn't match argument type");
+             error ("Format specifier doesn't match argument type");
            thissize = CONVERTED_BYTE_SIZE (multibyte, args[n]);
          }
        /* Would get MPV otherwise, since Lisp_Int's `point' to low memory.  */